
#include "gh.h"
#include <stdarg.h>

static char nErrorBuf[8 * 1024];

void ghTrace(const char *file, int line, const char *format, ...) {
    va_list args;
    fprintf(stdout, "[%s: %d] ", file, line);
    va_start(args, format);
    vfprintf(stdout, format, args);
    va_end(args);
}

void ghErrorSetInfo(const char *file, int line, const char *format, ...) {
    int offset = sprintf(nErrorBuf, "[%s: %d] ", file, line);
    if (offset > 0) {
        va_list args;
        va_start(args, format);
        vsnprintf(nErrorBuf + offset, sizeof(nErrorBuf) - offset, format, args);
        va_end(args);
    }
}

const char *ghErrorGetInfo(void) {
    return nErrorBuf;
}

int ghNVProgramSetString(GLenum target, const char *source) {
    GLenum error;
    const GLubyte *errorStr;

    glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei) strlen(source), source);
    error = glGetError();
    errorStr = glGetString(GL_PROGRAM_ERROR_STRING_ARB);

    if (error != GL_NO_ERROR) {
        ghErrorSetInfo(__FILE__, __LINE__, "OpenGL program source is invalid. Error String:\n%s", errorStr);
        return -1;
    }

#if GH_APP_TRACE == 1
    if (errorStr[0] != 0) {
        ghTrace(__FILE__, __LINE__, "OpenGL program Error String:\n%s\n", errorStr);
    }
#endif

    return 0;
}

static int nABMoving = 0;
static GHvecF32 nABQuatNow = {{{ 0.f, 0.f, 0.f, 1.f }}};
static GHvecF32 nABQuatDown = {{{ 0.f, 0.f, 0.f, 1.f }}};
static GHvecF32 nABVecNow, nABVecDown;

static GHvec ScreenToVector(float screenX, float screenY) {
    GHvec v, r;

    screenX = ((screenX / ((GH_APP_VIEWPORT_WIDTH - 1) / 2)) - 1);
    screenY = -((screenY / ((GH_APP_VIEWPORT_HEIGHT - 1) / 2)) - 1);

    v = ghVecSet(screenX, screenY, 0.f, 0.f);
    r = ghVec2Length(v);

    if (ghVec4Greater(r, ghVecOne())) {
        r = ghVecRcpSqrtF(r);
        r = ghVecMul(v, r);
        return r;
    }

    r = ghVecSub(ghVecOne(), r);
    r = ghVecSqrt(r);
    r = ghVecSelect(v, r, ghVecSelectMask(0, 0, 1, 0));
    return r;
}

void ghArcBallBegin(int x, int y) {
    nABMoving = 1;
    nABVecDown.v = ScreenToVector((float) x, (float) y);
    nABQuatDown = nABQuatNow;
}

void ghArcBallEnd(void) {
    nABMoving = 0;
}

void ghArcBallMove(int x, int y) {
    if (nABMoving) {
        GHvec p, l;
        nABVecNow.v = ScreenToVector((float) x, (float) y);

        p = ghVec3Cross(nABVecDown.v, nABVecNow.v);
        l = ghVec3Length(p);

        if (ghVec4Greater(l, ghVecEpsilon())) {
            GHvec q, d;
            d = ghVec3Dot(nABVecDown.v, nABVecNow.v);
            q = ghVecSelect(p, d, ghVecSelectMask(0, 0, 0, 1));
            q = ghVec4NormalizeF(q);
            nABQuatNow.v = ghQuatMul(q, nABQuatDown.v);
        }
    }
}

GHmat ghArcBallGetMat(void) {
    return ghMatRotationQuat(nABQuatNow.v);
}

/*
const char *ghGLProgramGetInfo(GLenum target) {
    static char info[
    GLint i, m;

    glGetProgramivARB(target, GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, &i);
    ghLog("MAX_PROGRAM_LOCAL_PARAMETERS: %d\n", i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, &i);
    ghLog("MAX_PROGRAM_ENV_PARAMETERS: %d\n", i);

    glGetProgramivARB(target, GL_PROGRAM_INSTRUCTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_INSTRUCTIONS_ARB, &m);
    ghLog("PROGRAM_INSTRUCTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_ALU_INSTRUCTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB, &m);
    ghLog("PROGRAM_ALU_INSTRUCTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_TEX_INSTRUCTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB, &m);
    ghLog("PROGRAM_TEX_INSTRUCTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_TEX_INDIRECTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB, &m);
    ghLog("PROGRAM_TEX_INDIRECTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_TEMPORARIES_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_TEMPORARIES_ARB, &m);
    ghLog("PROGRAM_TEMPORARIES: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_PARAMETERS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_PARAMETERS_ARB, &m);
    ghLog("PROGRAM_PARAMETERS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_ATTRIBS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_ATTRIBS_ARB, &m);
    ghLog("PROGRAM_ATTRIBS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_ADDRESS_REGISTERS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, &m);
    ghLog("PROGRAM_ADDRESS_REGISTERS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, &m);
    ghLog("PROGRAM_NATIVE_INSTRUCTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, &m);
    ghLog("PROGRAM_NATIVE_ALU_INSTRUCTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, &m);
    ghLog("PROGRAM_NATIVE_TEX_INSTRUCTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB, &m);
    ghLog("PROGRAM_NATIVE_TEX_INDIRECTIONS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_TEMPORARIES_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, &m);
    ghLog("PROGRAM_NATIVE_TEMPORARIES: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_PARAMETERS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB, &m);
    ghLog("PROGRAM_NATIVE_PARAMETERS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_ATTRIBS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB, &m);
    ghLog("PROGRAM_NATIVE_ATTRIBS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB, &m);
    ghLog("PROGRAM_NATIVE_ADDRESS_REGISTERS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_ATTRIB_COMPONENTS_NV, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV, &m);
    ghLog("PROGRAM_ATTRIB_COMPONENTS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_PROGRAM_RESULT_COMPONENTS_NV, &i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_RESULT_COMPONENTS_NV, &m);
    ghLog("PROGRAM_RESULT_COMPONENTS: %d / %d\n", i, m);

    glGetProgramivARB(target, GL_MAX_PROGRAM_IF_DEPTH_NV, &i);
    ghLog("MAX_PROGRAM_IF_DEPTH: %d\n", i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_LOOP_DEPTH_NV, &i);
    ghLog("MAX_PROGRAM_LOOP_DEPTH: %d\n", i);
    glGetProgramivARB(target, GL_MAX_PROGRAM_CALL_DEPTH_NV, &i);
    ghLog("MAX_PROGRAM_CALL_DEPTH: %d\n", i);
}
*/
